From e43071c7b1a81fe80595bb6a42214d1029e8a88d Mon Sep 17 00:00:00 2001 From: "vhanquez@kneesa.uk.xensource.com" Date: Thu, 13 Apr 2006 16:21:13 +0000 Subject: [PATCH] add quota to xenstored. Signed-off-by: Vincent Hanquez --- tools/xenstore/xenstored_core.c | 46 +++++++++++++++++++++++---- tools/xenstore/xenstored_domain.c | 52 +++++++++++++++++++++++++++++++ tools/xenstore/xenstored_domain.h | 8 +++++ tools/xenstore/xenstored_watch.c | 9 ++++++ 4 files changed, 109 insertions(+), 6 deletions(-) diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c index f4fa447d07..0fa08af504 100644 --- a/tools/xenstore/xenstored_core.c +++ b/tools/xenstore/xenstored_core.c @@ -77,6 +77,10 @@ static void check_store(void); } while (0) +int quota_nb_entry_per_domain = 1000; +int quota_nb_watch_per_domain = 128; +int quota_max_entry_size = 2048; /* 2K */ + #ifdef TESTING static bool failtest = false; @@ -455,6 +459,10 @@ static bool write_node(struct connection *conn, const struct node *node) data.dsize = 3*sizeof(uint32_t) + node->num_perms*sizeof(node->perms[0]) + node->datalen + node->childlen; + + if (data.dsize >= quota_max_entry_size) + goto error; + data.dptr = talloc_size(node, data.dsize); ((uint32_t *)data.dptr)[0] = node->num_perms; ((uint32_t *)data.dptr)[1] = node->datalen; @@ -470,10 +478,12 @@ static bool write_node(struct connection *conn, const struct node *node) /* TDB should set errno, but doesn't even set ecode AFAICT. */ if (tdb_store(tdb_context(conn), key, data, TDB_REPLACE) != 0) { corrupt(conn, "Write of %s = %s failed", key, data); - errno = ENOSPC; - return false; + goto error; } return true; + error: + errno = ENOSPC; + return false; } static enum xs_perm_type perm_for_conn(struct connection *conn, @@ -765,8 +775,11 @@ static void delete_node_single(struct connection *conn, struct node *node) key.dptr = (void *)node->name; key.dsize = strlen(node->name); - if (tdb_delete(tdb_context(conn), key) != 0) + if (tdb_delete(tdb_context(conn), key) != 0) { corrupt(conn, "Could not delete '%s'", node->name); + return; + } + domain_entry_dec(conn); } /* Must not be / */ @@ -788,7 +801,10 @@ static struct node *construct_node(struct connection *conn, const char *name) parent = construct_node(conn, parentname); if (!parent) return NULL; - + + if (domain_entry(conn) >= quota_nb_entry_per_domain) + return NULL; + /* Add child to parent. */ base = basename(name); baselen = strlen(base) + 1; @@ -814,6 +830,7 @@ static struct node *construct_node(struct connection *conn, const char *name) node->children = node->data = NULL; node->childlen = node->datalen = 0; node->parent = parent; + domain_entry_inc(conn); return node; } @@ -848,8 +865,10 @@ static struct node *create_node(struct connection *conn, /* We write out the nodes down, setting destructor in case * something goes wrong. */ for (i = node; i; i = i->parent) { - if (!write_node(conn, i)) + if (!write_node(conn, i)) { + domain_entry_dec(conn); return NULL; + } talloc_set_destructor(i, destroy_node); } @@ -1706,6 +1725,9 @@ static void usage(void) " --no-fork to request that the daemon does not fork,\n" " --output-pid to request that the pid of the daemon is output,\n" " --trace-file giving the file for logging, and\n" +" --entry-nb limit the number of entries per domain,\n" +" --entry-size limit the size of entry per domain, and\n" +" --entry-watch limit the number of watches per domain,\n" " --no-recovery to request that no recovery should be attempted when\n" " the store is corrupted (debug only),\n" " --preserve-local to request that /local is preserved on start-up,\n" @@ -1715,14 +1737,17 @@ static void usage(void) static struct option options[] = { { "no-domain-init", 0, NULL, 'D' }, + { "entry-nb", 1, NULL, 'E' }, { "pid-file", 1, NULL, 'F' }, { "help", 0, NULL, 'H' }, { "no-fork", 0, NULL, 'N' }, { "output-pid", 0, NULL, 'P' }, + { "entry-size", 1, NULL, 'S' }, { "trace-file", 1, NULL, 'T' }, { "no-recovery", 0, NULL, 'R' }, { "preserve-local", 0, NULL, 'L' }, { "verbose", 0, NULL, 'V' }, + { "watch-nb", 1, NULL, 'W' }, { NULL, 0, NULL, 0 } }; extern void dump_conn(struct connection *conn); @@ -1737,12 +1762,15 @@ int main(int argc, char *argv[]) bool no_domain_init = false; const char *pidfile = NULL; - while ((opt = getopt_long(argc, argv, "DF:HNPT:RLV", options, + while ((opt = getopt_long(argc, argv, "DE:F:HNPS:T:RLVW:", options, NULL)) != -1) { switch (opt) { case 'D': no_domain_init = true; break; + case 'E': + quota_nb_entry_per_domain = strtol(optarg, NULL, 10); + break; case 'F': pidfile = optarg; break; @@ -1761,12 +1789,18 @@ int main(int argc, char *argv[]) case 'L': remove_local = false; break; + case 'S': + quota_max_entry_size = strtol(optarg, NULL, 10); + break; case 'T': tracefile = optarg; break; case 'V': verbose = true; break; + case 'W': + quota_nb_watch_per_domain = strtol(optarg, NULL, 10); + break; } } if (optind != argc) diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c index 27f906dda6..0de6f99c17 100644 --- a/tools/xenstore/xenstored_domain.c +++ b/tools/xenstore/xenstored_domain.c @@ -74,6 +74,12 @@ struct domain /* Have we noticed that this domain is shutdown? */ int shutdown; + + /* number of entry from this domain in the store */ + int nbentry; + + /* number of watch for this domain */ + int nbwatch; }; static LIST_HEAD(domains); @@ -285,6 +291,8 @@ static struct domain *new_domain(void *context, unsigned int domid, domain->conn->id = domid; domain->remote_port = port; + domain->nbentry = 0; + domain->nbwatch = 0; return domain; } @@ -562,6 +570,50 @@ int domain_init(void) return eventchn_fd; } +void domain_entry_inc(struct connection *conn) +{ + if (!conn || !conn->domain) + return; + conn->domain->nbentry++; +} + +void domain_entry_dec(struct connection *conn) +{ + if (!conn || !conn->domain) + return; + if (conn->domain->nbentry) + conn->domain->nbentry--; +} + +int domain_entry(struct connection *conn) +{ + return (conn && conn->domain && conn->domain->domid) + ? conn->domain->nbentry + : 0; +} + +void domain_watch_inc(struct connection *conn) +{ + if (!conn || !conn->domain) + return; + conn->domain->nbwatch++; +} + +void domain_watch_dec(struct connection *conn) +{ + if (!conn || !conn->domain) + return; + if (conn->domain->nbwatch) + conn->domain->nbwatch--; +} + +int domain_watch(struct connection *conn) +{ + return (conn && conn->domain && conn->domain->domid) + ? conn->domain->nbwatch + : 0; +} + /* * Local variables: * c-file-style: "linux" diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h index b8d839d624..e350746639 100644 --- a/tools/xenstore/xenstored_domain.h +++ b/tools/xenstore/xenstored_domain.h @@ -47,4 +47,12 @@ void restore_existing_connections(void); bool domain_can_read(struct connection *conn); bool domain_can_write(struct connection *conn); +/* Quota manipulation */ +void domain_entry_inc(struct connection *conn); +void domain_entry_dec(struct connection *conn); +int domain_entry(struct connection *conn); +void domain_watch_inc(struct connection *conn); +void domain_watch_dec(struct connection *conn); +int domain_watch(struct connection *conn); + #endif /* _XENSTORED_DOMAIN_H */ diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c index 6f3c2e4e03..3de6e7ace8 100644 --- a/tools/xenstore/xenstored_watch.c +++ b/tools/xenstore/xenstored_watch.c @@ -32,6 +32,8 @@ #include "xenstored_test.h" #include "xenstored_domain.h" +extern int quota_nb_watch_per_domain; + struct watch { /* Watches on this connection */ @@ -135,6 +137,11 @@ void do_watch(struct connection *conn, struct buffered_data *in) } } + if (domain_watch(conn) > quota_nb_watch_per_domain) { + send_error(conn, E2BIG); + return; + } + watch = talloc(conn, struct watch); watch->node = talloc_strdup(watch, vec[0]); watch->token = talloc_strdup(watch, vec[1]); @@ -145,6 +152,7 @@ void do_watch(struct connection *conn, struct buffered_data *in) INIT_LIST_HEAD(&watch->events); + domain_watch_inc(conn); list_add_tail(&watch->list, &conn->watches); trace_create(watch, "watch"); talloc_set_destructor(watch, destroy_watch); @@ -169,6 +177,7 @@ void do_unwatch(struct connection *conn, struct buffered_data *in) if (streq(watch->node, node) && streq(watch->token, vec[1])) { list_del(&watch->list); talloc_free(watch); + domain_watch_dec(conn); send_ack(conn, XS_UNWATCH); return; } -- 2.30.2